package audio;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Vector;

import javax.sound.sampled.AudioFileFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.UnsupportedAudioFileException;

import org.tritonus.share.sampled.file.TAudioFileFormat;

import utils.FileUtil;

public class MP3 extends AudioFile {
	protected int channels = -1;

	protected String channelsMode = null;

	protected String version = null;

	protected int rate = 0;

	protected String layer = null;

	protected String emphasis = null;

	protected int nominalbitrate = 0;

	protected long total = 0;

	protected String vendor = null;

	protected String location = null;

	protected long size = 0;

	protected boolean copyright = false;

	protected boolean crc = false;

	protected boolean original = false;

	protected boolean priv = false;

	protected boolean vbr = false;

	protected String year = null;

	protected Vector comments = null;

	public MP3(File file) throws IOException {
		super(file);
		size = file.length();
		location = file.getCanonicalPath();
		try {
			load(file);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (UnsupportedAudioFileException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		formatedDuration = FileUtil.getFormatedDuration(this.getPlayTime());
	}

	/**
	 * Load and parse MPEG info from File.
	 * 
	 * @param input
	 * @throws IOException
	 */
	public void load(File input) throws IOException,
			UnsupportedAudioFileException {
		size = input.length();
		location = input.getCanonicalPath();
		loadInfo(input);
	}

	/**
	 * Load and parse MPEG info from URL.
	 * 
	 * @param input
	 * @throws IOException
	 * @throws UnsupportedAudioFileException
	 */
	public void load(URL input) throws IOException,
			UnsupportedAudioFileException {
		location = input.toString();
		loadInfo(input);
	}

	/**
	 * Load and parse MPEG info from InputStream.
	 * 
	 * @param input
	 * @throws IOException
	 * @throws UnsupportedAudioFileException
	 */
	public void load(InputStream input) throws IOException,
			UnsupportedAudioFileException {
		loadInfo(input);
	}

	/**
	 * Load info from input stream.
	 * 
	 * @param input
	 * @throws IOException
	 * @throws UnsupportedAudioFileException
	 */
	protected void loadInfo(InputStream input) throws IOException,
			UnsupportedAudioFileException {
		AudioFileFormat aff = AudioSystem.getAudioFileFormat(input);
		loadInfo(aff);
	}

	/**
	 * Load MP3 info from file.
	 * 
	 * @param file
	 * @throws IOException
	 * @throws UnsupportedAudioFileException
	 */
	protected void loadInfo(File file) throws IOException,
			UnsupportedAudioFileException {
		AudioFileFormat aff = AudioSystem.getAudioFileFormat(file);
		loadInfo(aff);
	}

	/**
	 * Load info from AudioFileFormat.
	 * 
	 * @param aff
	 */
	protected void loadInfo(AudioFileFormat aff)
			throws UnsupportedAudioFileException {
		String type = aff.getType().toString();
		if (!type.equalsIgnoreCase("mp3"))
			throw new UnsupportedAudioFileException("Not MP3 audio format");
		if (aff instanceof TAudioFileFormat) {
			Map props = ((TAudioFileFormat) aff).properties();
			if (props.containsKey("mp3.channels"))
				channels = ((Integer) props.get("mp3.channels")).intValue();
			if (props.containsKey("mp3.frequency.hz"))
				rate = ((Integer) props.get("mp3.frequency.hz")).intValue();
			if (props.containsKey("mp3.bitrate.nominal.bps"))
				nominalbitrate = ((Integer) props
						.get("mp3.bitrate.nominal.bps")).intValue();
			if (props.containsKey("mp3.version.layer"))
				layer = "Layer " + props.get("mp3.version.layer");
			if (props.containsKey("mp3.version.mpeg")) {
				version = (String) props.get("mp3.version.mpeg");
				if (version.equals("1"))
					version = "MPEG1";
				else if (version.equals("2"))
					version = "MPEG2-LSF";
				else if (version.equals("2.5"))
					version = "MPEG2.5-LSF";
			}
			if (props.containsKey("mp3.mode")) {
				int mode = ((Integer) props.get("mp3.mode")).intValue();
				if (mode == 0)
					channelsMode = "Stereo";
				else if (mode == 1)
					channelsMode = "Joint Stereo";
				else if (mode == 2)
					channelsMode = "Dual Channel";
				else if (mode == 3)
					channelsMode = "Single Channel";
			}
			if (props.containsKey("mp3.crc"))
				crc = ((Boolean) props.get("mp3.crc")).booleanValue();
			if (props.containsKey("mp3.vbr"))
				vbr = ((Boolean) props.get("mp3.vbr")).booleanValue();
			if (props.containsKey("mp3.copyright"))
				copyright = ((Boolean) props.get("mp3.copyright"))
						.booleanValue();
			if (props.containsKey("mp3.original"))
				original = ((Boolean) props.get("mp3.original")).booleanValue();
			emphasis = "none";
			if (props.containsKey("title"))
				title = (String) props.get("title");
			if (props.containsKey("author"))
				artist = (String) props.get("author");
			if (props.containsKey("album"))
				album = (String) props.get("album");
			if (props.containsKey("date"))
				year = (String) props.get("date");
			if (props.containsKey("duration"))
				total = (long) Math.round((((Long) props.get("duration"))
						.longValue()) / 1000000);
			if (props.containsKey("mp3.id3tag.genre"))
				genre = (String) props.get("mp3.id3tag.genre");
			if (props.containsKey("mp3.id3tag.track")) {
				try {
					track = Integer.parseInt((String) props
							.get("mp3.id3tag.track"));

				} catch (NumberFormatException e1) {
					// Not a number , perhaps it has a "/" in it like 09/15
					int i = ((String) props.get("mp3.id3tag.track"))
							.indexOf("/");
					if (i > 0) {
						try {
							String s = props.get("mp3.id3tag.track").toString()
									.substring(0, i);
							track = Integer.parseInt(s);
						} catch (NumberFormatException e2) {

						}
					}
				}
			}
		}
	}

	/**
	 * Load MP3 info from URL.
	 * 
	 * @param input
	 * @throws IOException
	 * @throws UnsupportedAudioFileException
	 */
	protected void loadInfo(URL input) throws IOException,
			UnsupportedAudioFileException {
		AudioFileFormat aff = AudioSystem.getAudioFileFormat(input);
		loadInfo(aff);
		loadShoutastInfo(aff);
	}

	/**
	 * Load Shoutcast info from AudioFileFormat.
	 * 
	 * @param aff
	 * @throws IOException
	 * @throws UnsupportedAudioFileException
	 */
	protected void loadShoutastInfo(AudioFileFormat aff) throws IOException,
			UnsupportedAudioFileException {
		String type = aff.getType().toString();
		if (!type.equalsIgnoreCase("mp3"))
			throw new UnsupportedAudioFileException("Not MP3 audio format");
		if (aff instanceof TAudioFileFormat) {
			Map props = ((TAudioFileFormat) aff).properties();
			// Try shoutcast meta data (if any).
			Iterator it = props.keySet().iterator();
			comments = new Vector();
			while (it.hasNext()) {
				String key = (String) it.next();
				if (key.startsWith("mp3.shoutcast.metadata.")) {
					String value = (String) props.get(key);
					key = key.substring(23, key.length());
					if (key.equalsIgnoreCase("icy-name")) {
						title = value;
					} else if (key.equalsIgnoreCase("icy-genre")) {
						genre = value;
					} else {
						comments.add(key + "=" + value);
					}
				}
			}
		}
	}

	public boolean getVBR() {
		return vbr;
	}

	public int getChannels() {
		return channels;
	}

	public String getVersion() {
		return version;
	}

	public String getEmphasis() {
		return emphasis;
	}

	public boolean getCopyright() {
		return copyright;
	}

	public boolean getCRC() {
		return crc;
	}

	public boolean getOriginal() {
		return original;
	}

	public String getLayer() {
		return layer;
	}

	public long getSize() {
		return size;
	}

	public String getLocation() {
		return location;
	}

	/*-- TagInfo Implementation --*/
	public int getSamplingRate() {
		return rate;
	}

	public int getBitRate() {
		return nominalbitrate;
	}

	public long getPlayTime() {
		return total;
	}

	public String getTitle() {
		return title;
	}

	public String getArtist() {
		return artist;
	}

	public String getAlbum() {
		return album;
	}

	public int getTrack() {
		return track;
	}

	public String getGenre() {
		return genre;
	}

	public Vector getComment() {
		return comments;
	}

	public String getYear() {
		return year;
	}

	/**
	 * Get channels mode.
	 * 
	 * @return channels mode
	 */
	public String getChannelsMode() {
		return channelsMode;
	}

}
